package net.xdclass.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.config.AccountConfig;
import net.xdclass.controller.req.*;
import net.xdclass.dto.*;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.ShareDayTypeEnum;
import net.xdclass.enums.ShareStatusEnum;
import net.xdclass.enums.ShareTypeEnum;
import net.xdclass.exception.BizException;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.*;
import net.xdclass.model.*;
import net.xdclass.service.AccountFileService;
import net.xdclass.service.ShareService;
import net.xdclass.util.JwtUtil;
import net.xdclass.util.SpringBeanUtil;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 姜阿木
 * @date 2025/7/22 15:19
 */
@Service
@Slf4j
public class ShareServiceImpl implements ShareService {

    @Autowired
    private ShareMapper shareMapper;

    @Autowired
    private AccountFileService fileService;
    @Autowired
    private ShareFileMapper shareFileMapper;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private AccountFileMapper accountFileMapper;

    @Override
    @Transactional
    public List<ShareDTO> listShare() {
        Long accountId = LoginInterceptor.threadLocal.get().getId();

        List<ShareDO> ShareDoList = shareMapper.selectList(new QueryWrapper<ShareDO>()
                .eq("account_id", accountId).orderByDesc("gmt_create"));
        return SpringBeanUtil.copyProperties(ShareDoList, ShareDTO.class);
    }

    /**
     * 检查文件分享的权限
     * 生成分享链接和持久化到数据库
     * 生成分享详情和持久化到数据库
     * @param req
     * @return
     */
    @Override
    public ShareDTO createShare(ShareCreateReq req) {
        // 1、检查文件权限
        List<Long> fileIds = req.getFileIds();
        fileService.checkFileIDLegal(fileIds, req.getAccountId());
        // 2、生成分享链接和持久化到数据库
        Integer dayType = req.getShareDayType();
        Integer shareDays = ShareDayTypeEnum.getDayByType(dayType);
        long shareId = IdUtil.getSnowflakeNextId();
        // 生成分享链接
        String shareUrl = AccountConfig.PAN_FRONT_DOMAIN_SHARE_API + shareId;
        log.info("生成分享链接:{}", shareUrl);
        // 持久化到数据库
        ShareDO shareDO = new ShareDO();
        shareDO.setId(shareId);
        shareDO.setShareName(req.getShareName());
        shareDO.setShareType(ShareTypeEnum.valueOf(req.getShareType()).name());
        shareDO.setShareDayType(dayType);
        shareDO.setShareDay(shareDays);
        shareDO.setShareUrl(shareUrl);
        shareDO.setShareStatus(ShareStatusEnum.USED.name());
        shareDO.setAccountId(req.getAccountId());

        if (ShareDayTypeEnum.PERMANENT.getDayType().equals(dayType)){
            shareDO.setShareEndTime(Date.from(LocalDate.of(9999, 1, 1)
                    .atStartOfDay(ZoneId.systemDefault()).toInstant()));
        }else {
            shareDO.setShareEndTime(new Date(System.currentTimeMillis() + shareDays * 24 * 60 * 60 * 1000));
        }
        // 生成分享码
        if (ShareTypeEnum.NEED_CODE.name().equalsIgnoreCase(req.getShareType())){
            String shareCode = RandomStringUtils.randomAlphabetic(6);
            shareDO.setShareCode(shareCode);
        }
        shareMapper.insert(shareDO);

        // 3、生成分享文件详情和持久化到数据库
        List<ShareFileDO> shareFileDOS = new ArrayList<>();
        for (Long fileId : fileIds) {
            ShareFileDO shareFileDO = new ShareFileDO();
            shareFileDO.setShareId(shareId);
            shareFileDO.setAccountFileId(fileId);
            shareFileDO.setAccountId(req.getAccountId());
            shareFileDOS.add(shareFileDO);
        }
        shareFileMapper.insertBatch(shareFileDOS);

        return SpringBeanUtil.copyProperties(shareDO, ShareDTO.class);
    }

    @Override
    public void cancelShare(ShareCancelReq req) {
        List<ShareDO> shareDOS = shareMapper.selectList(new QueryWrapper<ShareDO>()
                .eq("accountId", req.getAccountId())
                .in("id", req.getShareIds()));

        if (shareDOS.size() != req.getShareIds().size()){
            throw new BizException(BizCodeEnum.SHARE_CANCEL_ILLEGAL);
        }
        // 删除分享链接
        shareMapper.deleteBatchIds(req.getShareIds());
        // 删除分享文件详情
        shareFileMapper.delete(new QueryWrapper<ShareFileDO>()
                .in("share_id", req.getShareIds()));
    }

    /**
     * 获取分享文件详情
     * * 检查分享状态
     * * 记录分享实体
     * * 查询分享者信息
     * * 判断是否需要生成校验码,不需要的话可以直接生成分享token
     * @param shareId
     * @return
     */
    @Override
    public ShareSimpleDTO simpleDetail(Long shareId) {
        // 1、检查分享状态
        ShareDO  shareDO = checkShareStatus(shareId);
        // 记录分享实体
        ShareSimpleDTO shareSimpleDTO = SpringBeanUtil.copyProperties(shareDO, ShareSimpleDTO.class);

        // 2、查询分享者信息
        ShareAccountDTO shareAccountDto = getShareAccount(shareDO.getAccountId());
        shareSimpleDTO.setShareAccountDto(shareAccountDto);
        // 3、判断是否需要生成校验码,不需要的话可以直接生成分享token
        if (ShareTypeEnum.NO_CODE.name().equalsIgnoreCase(shareDO.getShareType())){
            shareSimpleDTO.setShareToken(JwtUtil.geneShareJWT(shareDO.getId()));
        }
        return shareSimpleDTO;
    }

    /**
     * 校验分享码
     * @param req
     * @return
     */
    @Override
    public String checkShareCode(ShareCheckReq req) {
        ShareDO shareDO = shareMapper.selectOne(new QueryWrapper<ShareDO>()
                .eq("id", req.getShareId())
                .eq("share_code", req.getShareCode())
                .eq("share_status", ShareStatusEnum.USED.name()));
        if (shareDO != null){
            // 判断是否过期
            if (shareDO.getShareEndTime().getTime() > System.currentTimeMillis()){
                return JwtUtil.geneShareJWT(shareDO.getId());
            }else {
                throw new BizException(BizCodeEnum.SHARE_EXPIRED);
            }
        }
        return null;
    }


    /**
     * 获取分享详情
     * * 查询分享记录实体
     * * 检查分享状态
     * * 查询分享文件信息
     * * 查询分享者信息
     * * 构造分析详情对象返回
     * @param shareId
     * @return
     */
    @Override
    public ShareDetailDTO detail(Long shareId) {
        // 1、查询分享记录实体
        ShareDO  shareDO = checkShareStatus(shareId);
        ShareDetailDTO shareDetailDTO = SpringBeanUtil.copyProperties(shareDO, ShareDetailDTO.class);
        // 3、查询分享文件信息
        List<AccountFileDO> accountFileDOList = getShareFileInfo(shareId);
        List<AccountFileDTO> accountFileDTOList = SpringBeanUtil.copyProperties(accountFileDOList, AccountFileDTO.class);
        shareDetailDTO.setFileList(accountFileDTOList);
        // 4、查询分享者信息
        ShareAccountDTO shareAccountDTO = getShareAccount(shareDO.getAccountId());
        shareDetailDTO.setShareAccountDto(shareAccountDTO);
        // 5、构造分析详情对象返回
        return shareDetailDTO;
    }

    /**
     * 获取分享文件列表,返回全部的子文件
     * * 检查分享链接状态
     * * 查询分享ID是否在分享的文件列表中（需要获取分享文件列表的全部文件夹和子文件夹）
     * * 分组后获取某个文件夹下面所有的子文件夹
     * * 根据父文件夹ID获取子文件夹列表
     * @param req
     * @return
     */
    @Override
    public List<AccountFileDTO> listShareFile(ShareFileQueryReq req) {
        // 1、检查分享链接状态
        ShareDO shareDO = checkShareStatus(req.getShareId());
        // 2、查询分享ID是否在分享的文件列表中
        List<AccountFileDO> accountFileDOList = checkShareFileIdOnStatus(req.getShareId(), List.of(req.getParentId()));
        List<AccountFileDTO> accountFileDTOList = SpringBeanUtil.copyProperties(accountFileDOList, AccountFileDTO.class);
        // 3、分组后获取某个文件夹下面所有的子文件夹
        Map<Long, List<AccountFileDTO>> fileListMap = accountFileDTOList
                .stream().collect(Collectors.groupingBy(AccountFileDTO::getParentId));
        // 4、根据父文件夹ID获取子文件夹列表
        List<AccountFileDTO> childFileList = fileListMap.get(req.getParentId());
        if (CollectionUtils.isEmpty(childFileList)){
            return List.of();
        }
        return childFileList;
    }

    /**
     * 文件分享转存
     * * 分享链接是否状态准确
     * * 转存的文件是否是分享链接里面的文件
     * * 目标文件夹是否是当前用户的
     * * 获取转存的文件
     * * 保存需要转存的文件列表（递归子文件）
     * * 同步更新所有文件的accountId为当前用户的id
     * * 计算存储空间大小，检查是否足够
     * * 更新关联对象信息，存储文件映射关系
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferShareFile(ShareFileTransferReq req) {
        // 1、分享链接是否状态准确
        ShareDO shareDO = checkShareStatus(req.getShareId());
        // 2、转存文件的是否是分享链接里面的文件
        checkInShareFiles(req.getFileIds(),req.getShareId());
        // 3、目标文件夹是否是当前用户的
        AccountFileDO accountFileDO = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>()
                .eq("id", req.getParentId()).eq("account_id", req.getAccountId()));
        if (accountFileDO == null){
            log.error("目标文件夹不存在");
            throw new BizException(BizCodeEnum.FILE_NOT_EXISTS);
        }
        // 4、获取转存文件
        List<AccountFileDO> shareFileList = accountFileMapper.selectBatchIds(req.getFileIds());
        // 5、保存需要转存文件列表（递归子文件）
        List<AccountFileDO> batchTransferFileList = fileService.findBatchCopyWithRecur(shareFileList, req.getParentId());
        // 6、同步更新所有文件的accountId为当前用户的id
        batchTransferFileList.forEach(newFileDO -> newFileDO.setAccountId(req.getAccountId()));
        // 7、计算存储空间大小，检查是否足够
        if (!fileService.checkAndUpdateCapacity(req.getAccountId(), batchTransferFileList
                .stream().map(accountFile -> accountFile.getFileSize() == null ? 0 : accountFile.getFileSize())
                .mapToLong(Long::valueOf).sum())){
            throw new BizException(BizCodeEnum.FILE_STORAGE_NOT_ENOUGH);
        }
        // 8、更新关联对象信息，存储文件映射关系
        accountFileMapper.insertFileBatch(batchTransferFileList);
    }

    /**
     * 检查分享文件是否在分享的文件列表中
     * @param fileIds
     * @param shareId
     */
    private void checkInShareFiles(List<Long> fileIds, Long shareId) {
        // 获取分享链接的文件列表
        List<ShareFileDO> shareFileDOList = shareFileMapper.selectList(new QueryWrapper<ShareFileDO>()
                .eq("share_id", shareId));
        List<Long> accountShareFileIds = shareFileDOList.stream().map(ShareFileDO::getAccountFileId).toList();
        // 获取文件实体对象
        List<AccountFileDO> accountFileDOList = accountFileMapper.selectBatchIds(accountShareFileIds);
        // 递归获取子文件，找出所有子文件
        List<AccountFileDO> allAccountFileDOList = new ArrayList<>();
        fileService.findAllAccountFileDOWithRecur(allAccountFileDOList, accountFileDOList, false);
        // 遍历获取文件ID,判断是否在分享的文件列表中
        List<Long> allShareFileIds = allAccountFileDOList.stream().map(AccountFileDO::getId).toList();
        for (Long fileId : fileIds) {
            if (!allShareFileIds.contains(fileId)){
                log.error("文件id不在分享的文件列表中，{}",fileId);
                throw new BizException(BizCodeEnum.SHARE_FILE_ILLEGAL);
            }
        }


    }

    private List<AccountFileDO> checkShareFileIdOnStatus(Long shareId, List<Long> fileIdList) {
        // 获取全部分享文件列表的文件夹及子文件内容
        List<AccountFileDO> shareFileInfo = getShareFileInfo(shareId);
        List<AccountFileDO> allAccountFileDOList = new ArrayList<>();
        // 获取全部文件，递归
        fileService.findAllAccountFileDOWithRecur(allAccountFileDOList, shareFileInfo, false);

        if (CollectionUtils.isEmpty(allAccountFileDOList)){
            return List.of();
        }
        // 把分享的对象文件的全部文件夹放到集合里面，判断目标文件夹是否在分享的文件夹中
        Set<Long> allFileSet = allAccountFileDOList.stream().map(AccountFileDO::getId).collect(Collectors.toSet());
        if (!allFileSet.containsAll(fileIdList)){
            log.error("目标文件id，不在分享的文件列表中，{}",fileIdList);
            throw new BizException(BizCodeEnum.SHARE_FILE_ILLEGAL);
        }
        return allAccountFileDOList;
    }

    // 获取分享文件信息
    private List<AccountFileDO> getShareFileInfo(Long shareId) {
        // 获取分享文件信息
        List<Long> shareFileIds = shareFileMapper.selectList(new QueryWrapper<ShareFileDO>()
                .eq("share_id", shareId))
                .stream().map(ShareFileDO::getAccountFileId).toList();
        // 获取文件对象
        return accountFileMapper.selectBatchIds(shareFileIds);
    }

    // 获取分享者信息
    private ShareAccountDTO getShareAccount(Long accountId) {
        if (accountId != null) {
            AccountDO accountDO = accountMapper.selectById(accountId);
            if (accountDO != null) {
                ShareAccountDTO shareAccountDTO  = SpringBeanUtil.copyProperties(accountDO, ShareAccountDTO.class);
                return shareAccountDTO;
            }
        }
        return null;

    }


    // 检查分享状态
    private ShareDO checkShareStatus(Long shareId) {
        ShareDO shareDO = shareMapper.selectById(shareId);
        if (shareDO == null){
            log.error("分享不存在:{}", shareId);
            throw new BizException(BizCodeEnum.SHARE_NOT_EXIST);
        }
        // 暂时未用，直接物理删除，可以调整
        if (ShareStatusEnum.CANCELED.name().equalsIgnoreCase(shareDO.getShareStatus())){
            log.error("分享已取消:{}", shareId);
            throw new BizException(BizCodeEnum.SHARE_CANCELD);
        }
        // 检查分享是否过期
        if (shareDO.getShareEndTime().before(new Date())){
            log.error("分享已过期:{}", shareId);
            throw new BizException(BizCodeEnum.SHARE_EXPIRED);
        }
        return shareDO;
    }
}
